<template>
  <div class="feature-extraction-container">
    <el-steps :active="step" finish-status="success" align-center>
      <el-step title="数据导入与清洗"></el-step>
      <el-step title="归一化配置"></el-step>
      <el-step title="特征提取"></el-step>
      <el-step title="特征预览"></el-step>
    </el-steps>

    <!-- 步骤一：数据导入与清洗 -->
    <div v-show="step === 0" class="step-panel">
      <el-card>
        <div class="section-title">参数选择</div>
        <el-date-picker
          v-model="importRange"
          type="daterange"
          :picker-options="pickerOptions"
          range-separator="至"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          style="margin-bottom: 16px;"
        ></el-date-picker>
        <el-checkbox-group v-model="selectedParams">
          <el-checkbox label="振动"></el-checkbox>
          <el-checkbox label="温度"></el-checkbox>
          <el-checkbox label="压力"></el-checkbox>
        </el-checkbox-group>
        <el-button
          type="primary"
          style="margin-left: 16px;"
          @click="handleImport"
        >开始导入</el-button>
      </el-card>
      <el-card v-if="rawData.length" style="margin-top: 24px;">
        <div class="section-title">清洗后数据</div>
        <el-table :data="cleanedData" style="width: 100%;" size="mini">
          <el-table-column prop="time" label="时间" width="180"></el-table-column>
          <el-table-column
            v-for="param in selectedParams"
            :key="param"
            :prop="param"
            :label="param"
          ></el-table-column>
        </el-table>
      </el-card>
      <div class="step-btns">
        <el-button type="primary" @click="nextStep" :disabled="!cleanedData.length">下一步</el-button>
      </div>
    </div>

    <!-- 步骤二：归一化配置 -->
    <div v-show="step === 1" class="step-panel">
      <el-card>
        <div class="section-title">归一化方法</div>
        <el-radio-group v-model="normMethod">
          <el-radio label="minmax">Min-Max 标准化</el-radio>
        </el-radio-group>
        <el-button
          type="primary"
          style="margin-left: 16px;"
          @click="handleNormalize"
        >执行归一化</el-button>
      </el-card>
      <el-card v-if="normalizedData.length" style="margin-top: 24px;">
        <div class="section-title">归一化后数据</div>
        <el-table :data="normalizedData" style="width: 100%;" size="mini">
          <el-table-column prop="time" label="时间" width="180"></el-table-column>
          <el-table-column
            v-for="param in selectedParams"
            :key="param"
            :prop="param"
            :label="param"
          ></el-table-column>
        </el-table>
      </el-card>
      <div class="step-btns">
        <el-button @click="prevStep">上一步</el-button>
        <el-button type="primary" @click="nextStep" :disabled="!normalizedData.length">下一步</el-button>
      </div>
    </div>

    <!-- 步骤三：特征提取 -->
    <div v-show="step === 2" class="step-panel">
      <el-card>
        <div class="section-title">特征提取方法</div>
        <el-button type="primary" @click="handleFFT" :disabled="!normalizedData.length">傅里叶变换</el-button>
        <el-button type="primary" @click="handleWavelet" :disabled="!normalizedData.length" style="margin-left: 16px;">小波变换</el-button>
        <el-input-number
          v-model="waveletLevel"
          :min="1"
          :max="8"
          label="分解层数"
          style="margin-left: 16px;"
        ></el-input-number>
      </el-card>
      <el-card v-if="features.length" style="margin-top: 24px;">
        <div class="section-title">特征向量</div>
        <el-table :data="features" style="width: 100%;" size="mini">
          <el-table-column prop="name" label="特征名称"></el-table-column>
          <el-table-column prop="type" label="类型"></el-table-column>
          <el-table-column prop="value" label="值"></el-table-column>
          <el-table-column prop="status" label="状态">
            <template slot-scope="scope">
              <el-tag v-if="scope.row.status==='正常'" type="success">正常</el-tag>
              <el-tag v-else type="danger">异常</el-tag>
            </template>
          </el-table-column>
        </el-table>
      </el-card>
      <div class="step-btns">
        <el-button @click="prevStep">上一步</el-button>
        <el-button type="primary" @click="nextStep" :disabled="!features.length">下一步</el-button>
      </div>
    </div>

    <!-- 步骤四：特征预览 -->
    <div v-show="step === 3" class="step-panel">
      <el-card>
        <div class="section-title">特征列表</div>
        <el-table :data="features" style="width: 100%;" size="mini">
          <el-table-column prop="name" label="特征名称"></el-table-column>
          <el-table-column prop="type" label="类型"></el-table-column>
          <el-table-column prop="value" label="值"></el-table-column>
          <el-table-column prop="status" label="状态">
            <template slot-scope="scope">
              <el-tag v-if="scope.row.status==='正常'" type="success">正常</el-tag>
              <el-tag v-else type="danger">异常</el-tag>
            </template>
          </el-table-column>
        </el-table>
        <div style="margin-top: 16px;">
          <el-button @click="downloadCSV" type="primary">下载CSV</el-button>
          <el-button @click="validateFeatures" type="success" style="margin-left: 16px;">特征验证</el-button>
        </div>
        <div v-if="validateMsg" style="margin-top: 12px;">
          <el-alert :title="validateMsg" :type="validateType" show-icon></el-alert>
        </div>
      </el-card>
      <div class="step-btns">
        <el-button @click="prevStep">上一步</el-button>
      </div>
    </div>
  </div>
</template>

<script>
import { saveAs } from 'file-saver';

export default {
  name: 'DataFeatureExtraction',
  data() {
    return {
      step: 0,
      importRange: [this.getDateNDaysAgo(30), this.getDateNDaysAgo(0)],
      pickerOptions: {
        disabledDate: (time) => time.getTime() > Date.now(),
      },
      selectedParams: ['振动', '温度', '压力'],
      rawData: [],
      cleanedData: [],
      normMethod: 'minmax',
      normalizedData: [],
      features: [],
      waveletLevel: 5,
      validateMsg: '',
      validateType: 'info',
    };
  },
  methods: {
    // 获取N天前的日期
    getDateNDaysAgo(n) {
      const d = new Date();
      d.setDate(d.getDate() - n);
      return d;
    },
    // 生成 mock 时序数据
    generateMockData() {
      const [start, end] = this.importRange;
      const data = [];
      let cur = new Date(start);
      while (cur <= end) {
        const row = { time: cur.toISOString().slice(0, 19).replace('T', ' ') };
        if (this.selectedParams.includes('振动')) row['振动'] = +(60 + Math.random() * 10 + Math.sin(cur.getHours()) * 5).toFixed(2);
        if (this.selectedParams.includes('温度')) row['温度'] = +(40 + Math.random() * 5 + Math.cos(cur.getHours()) * 2).toFixed(2);
        if (this.selectedParams.includes('压力')) row['压力'] = +(2 + Math.random() * 0.5 + Math.sin(cur.getHours() / 2)).toFixed(2);
        data.push(row);
        cur.setHours(cur.getHours() + 1);
      }
      // 随机插入噪声点和缺失值
      for (let i = 0; i < data.length; i++) {
        if (Math.random() < 0.01) { // 1% 概率为噪声点
          this.selectedParams.forEach(p => data[i][p] = +(data[i][p] + (Math.random() > 0.5 ? 30 : -30)).toFixed(2));
        }
        if (Math.random() < 0.01) { // 1% 概率为缺失
          this.selectedParams.forEach(p => { if (Math.random() < 0.5) data[i][p] = null; });
        }
      }
      return data;
    },
    // 数据导入与清洗
    handleImport() {
      this.rawData = this.generateMockData();
      this.cleanedData = this.cleanData(this.rawData);
      this.saveToLocal('cleanedData', this.cleanedData);
      this.$message.success('数据导入并清洗完成');
    },
    // 数据清洗：去除噪声点、补齐缺失值
    cleanData(data) {
      // 去除噪声点（极端值处理：3σ原则）
      const params = this.selectedParams;
      const result = JSON.parse(JSON.stringify(data));
      params.forEach(param => {
        const values = result.map(r => r[param]).filter(v => v !== null && v !== undefined);
        const mean = values.reduce((a, b) => a + b, 0) / values.length;
        const std = Math.sqrt(values.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / values.length);
        result.forEach(r => {
          if (r[param] !== null && r[param] !== undefined) {
            if (Math.abs(r[param] - mean) > 3 * std) {
              r[param] = mean; // 用均值替换噪声点
            }
          }
        });
        // 补齐缺失值（线性插值）
        for (let i = 0; i < result.length; i++) {
          if (r[param] === null || r[param] === undefined) {
            // 找前后最近的非空
            let prev = i - 1, next = i + 1;
            while (prev >= 0 && (result[prev][param] === null || result[prev][param] === undefined)) prev--;
            while (next < result.length && (result[next][param] === null || result[next][param] === undefined)) next++;
            if (prev >= 0 && next < result.length) {
              result[i][param] = +((result[prev][param] + result[next][param]) / 2).toFixed(2);
            } else if (prev >= 0) {
              result[i][param] = result[prev][param];
            } else if (next < result.length) {
              result[i][param] = result[next][param];
            } else {
              result[i][param] = mean;
            }
          }
        }
      });
      return result;
    },
    // 归一化
    handleNormalize() {
      if (!this.cleanedData.length) return;
      const params = this.selectedParams;
      const minmax = {};
      params.forEach(param => {
        const values = this.cleanedData.map(r => r[param]);
        minmax[param] = {
          min: Math.min(...values),
          max: Math.max(...values),
        };
      });
      this.normalizedData = this.cleanedData.map(row => {
        const newRow = { time: row.time };
        params.forEach(param => {
          const { min, max } = minmax[param];
          newRow[param] = +((row[param] - min) / (max - min)).toFixed(4);
        });
        return newRow;
      });
      this.saveToLocal('normalizedData', this.normalizedData);
      this.$message.success('归一化完成');
    },
    // 傅里叶变换（mock主频、谐波分布）
    handleFFT() {
      if (!this.normalizedData.length) return;
      // mock: 主频=50.2Hz，谐波分布=若干随机值
      this.features = [
        { name: '主频', type: '频域', value: 50.2, status: '正常' },
        { name: '谐波分布', type: '频域', value: [0.12, 0.08, 0.05, 0.03].join(','), status: '正常' },
      ];
      this.saveToLocal('features', this.features);
      this.$message.success('傅里叶变换特征提取完成');
    },
    // 小波变换（mock峰值因子、峭度）
    handleWavelet() {
      if (!this.normalizedData.length) return;
      // mock: 峰值因子=1.23，峭度=2.1
      this.features = [
        { name: '峰值因子', type: '时域', value: 1.23, status: '正常' },
        { name: '峭度', type: '时域', value: 2.1, status: '异常' },
      ];
      this.saveToLocal('features', this.features);
      this.$message.success('小波变换特征提取完成');
    },
    // 特征验证
    validateFeatures() {
      if (!this.features.length) return;
      // mock: 缺失率<1%为通过
      const missing = this.features.filter(f => f.value === null || f.value === undefined).length;
      const rate = missing / this.features.length;
      if (rate < 0.01) {
        this.validateMsg = '特征完整性校验通过，已存入模型输入池';
        this.validateType = 'success';
        this.saveToLocal('modelInputPool', this.features);
      } else {
        this.validateMsg = '特征缺失率过高，校验未通过';
        this.validateType = 'error';
      }
    },
    // CSV下载
    downloadCSV() {
      if (!this.features.length) return;
      const header = '特征名称,类型,值,状态\n';
      const rows = this.features.map(f => `${f.name},${f.type},${f.value},${f.status}`).join('\n');
      const blob = new Blob([header + rows], { type: 'text/csv;charset=utf-8;' });
      if (window.navigator.msSaveBlob) {
        window.navigator.msSaveBlob(blob, 'features.csv');
      } else {
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.setAttribute('download', 'features.csv');
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
      }
    },
    // 步骤切换
    nextStep() {
      if (this.step < 3) this.step++;
    },
    prevStep() {
      if (this.step > 0) this.step--;
    },
    // localStorage 持久化
    saveToLocal(key, value) {
      localStorage.setItem('fdx_' + key, JSON.stringify(value));
    },
    loadFromLocal(key) {
      const v = localStorage.getItem('fdx_' + key);
      return v ? JSON.parse(v) : null;
    },
  },
  mounted() {
    // 自动加载上次数据
    const cleaned = this.loadFromLocal('cleanedData');
    if (cleaned) this.cleanedData = cleaned;
    const normed = this.loadFromLocal('normalizedData');
    if (normed) this.normalizedData = normed;
    const feats = this.loadFromLocal('features');
    if (feats) this.features = feats;
  },
};
</script>

<style scoped>
.feature-extraction-container {
  max-width: 1100px;
  margin: 0 auto;
  padding: 32px 0;
}
.section-title {
  font-weight: bold;
  font-size: 16px;
  margin-bottom: 12px;
}
.step-panel {
  margin-top: 32px;
}
.step-btns {
  margin-top: 24px;
  text-align: right;
}
</style>
